//
// Created by guoshunw on 1/28/2021.
//

#include <cstdio>
#include "Reference.h"


#define    SIGN_BIT    (0x80)        /* Sign bit for a A-law byte. */
#define    QUANT_MASK    (0xf)        /* Quantization field mask. */
#define    NSEGS        (8)            /* Number of A-law segments. */
#define    SEG_SHIFT    (4)            /* Left shift for segment number. */
#define    SEG_MASK    (0x70)        /* Segment field mask. */
#define    BIAS        (0x84)        /* Bias for linear code. */

static const int16_t seg_uend1[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
static const int    seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF,0x3FF, 0x7FF, 0xFFF, 0x1FFF};
static const int16_t seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF};


static int16_t search1(int16_t val, const int16_t *table, int16_t size);

/* 2's complement (16-bit range) */
uint8_t linear2alaw(int16_t pcm_val) {
    int16_t mask;
    int16_t seg;
    uint8_t aval;

    pcm_val = pcm_val >> 3;//这里右移3位，因为采样值是16bit，而A-law是13bit数据，存储在高13位上，低3位被舍弃

    if (pcm_val >= 0) {
        mask = 0xD5;//二进制的11010101
    } else {
        mask = 0x55;//二进制的01010101，与0xD5只有符号位的不同
        pcm_val = -pcm_val - 1;//负数转换为正数计算
    }

    seg = search1(pcm_val, seg_aend, 8);//查找采样值对应哪一段折线

    if (seg >= 8) {
        return (uint8_t) (0x7F);//越界时直接返回最大值
    } else {
//以下按照表格处理，低4位是数据，5~7位是指数，最高位是符号
        aval = (uint8_t) seg << SEG_SHIFT;
        if (seg < 2)
            aval |= (pcm_val >> 1) & QUANT_MASK;
        else
            aval |= (pcm_val >> seg) & QUANT_MASK;

//        printf("aval=%x, mask=%x\n", aval, mask);

        return aval ^ mask;
    }
}

int16_t alaw2linear(uint8_t a_val) {
    int16_t t;
    int16_t seg;

    a_val ^= 0x55;//异或操作把mask还原

    t = (a_val & QUANT_MASK) << 4;//取低4位，即上表中的abcd值，然后左移4位变成abcd0000
    seg = ((unsigned) a_val & SEG_MASK) >> SEG_SHIFT;//取中间3位，指数部分
    switch (seg) {
        case 0://表中第一行，abcd0000 -> abcd1000
            t += 8;
            break;
        case 1://表中第二行，abcd0000 -> 1abcd1000
            t += 0x108;
            break;
        default://表中其他行，abcd0000 -> 1abcd1000 的基础上继续左移
            t += 0x108;
            t <<= seg - 1;
            break;
    }
    return ((a_val & SIGN_BIT) ? t : -t);
}

static int16_t search1(int16_t val, const int16_t *table, int16_t size) {
    int i;

    for (i = 0; i < size; i++) {
        if (val <= *table++)
            return (i);
    }
    return (size);
}

static int search(
        int		val,	/* changed from "short" *drago* */
        const int *	table,
        int		size)	/* changed from "short" *drago* */
{
    int		i;		/* changed from "short" *drago* */

    for (i = 0; i < size; i++) {
        if (val <= *table++)
            return (i);
    }
    return (size);
}

#define BIAS        (0x84)      /* Bias for linear code. 线性码偏移值*/
#define CLIP            8159    //最大量化级数量

int16_t ulaw2linear(uint8_t u_val) {
    int16_t t;

    /* Complement to obtain normal u-law value. */
    u_val = ~u_val;

    /*
    * Extract and bias the quantization bits. Then
    * shift up by the segment number and subtract out the bias.
    */

    t = ((u_val & QUANT_MASK) << 3) + BIAS;
    t <<= ((unsigned) u_val & SEG_MASK) >> SEG_SHIFT;

    return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}

uint8_t linear2ulaw1(int16_t pcm_val)    /* 2's complement (16-bit range) */
{
    int16_t mask;
    int16_t seg;
    uint8_t uval;

    /* Get the sign and the magnitude of the value. */
    if (pcm_val < 0) {
        pcm_val = BIAS - pcm_val;
        mask = 0x7F;
    } else {
        pcm_val += BIAS;
        mask = 0xFF;
    }

    /* Convert the scaled magnitude to segment number. */
    seg = search1(pcm_val, seg_uend1, 8);

    /*
    * Combine the sign, segment, quantization bits;
    * and complement the code word.
    */
    if (seg >= 8)        /* out of range, return maximum value. */
        return (0x7F ^ mask);



    uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
    printf("seg=%d, uval=%0x\n", seg, uval);
    return (uval ^ mask);
}



int linear2ulaw( int	pcm_val)	/* 2's complement (16-bit range) */
{
    int		mask;
    int		seg;
    int		uval;

    /* Get the sign and the magnitude of the value. */
    pcm_val = pcm_val >> 2;
    if (pcm_val < 0) {
        pcm_val = -pcm_val;
        mask = 0x7F;
    } else {
        mask = 0xFF;
    }
    if ( pcm_val > CLIP ) pcm_val = CLIP;		/* clip the magnitude */
    pcm_val += (BIAS >> 2);

    /* Convert the scaled magnitude to segment number. */
    seg = search(pcm_val, seg_uend, 8);
    printf("seg=%d, pal_val=%x\n", seg, pcm_val);
    /*
     * Combine the sign, segment, quantization bits;
     * and complement the code word.
     */
    if (seg >= 8)		/* out of range, return maximum value. */
        return (0x7F ^ mask);
    else {
        uval = (seg << 4) | ((pcm_val >> (seg + 1)) & 0xF);
        return (uval ^ mask);
    }

}
